package rpg.visual;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;

import pgui.PVisual;
import pgui.visual.PComponent;
import rpg.map.Creature;
import rpg.map.Effect;
import rpg.map.Location;
import rpg.map.Map;
import rpg.map.MapObject;
import rpg.player.Player;
import square.data.DataGroup;
import square.data.ItemData;
import square.data.MapData;
import square.data.MapObjectData;

/**
 * 
 * @author Pedro Silva Moreira
 */
public class VisualMap extends PComponent {
	public static final int DISPLAY_WIDTH = 19, DISPLAY_HEIGHT = 15;

	// Local Variables
	private Map map;
	private MapObject centralObject;
	private Boolean scroll = false;

	// Creation
	public VisualMap(PVisual visual) {
		super(visual);

		this.resize();
		this.setClip(true);
	}

	public void resize() {
		setWidth(DISPLAY_WIDTH * VisualImage.SQUARE_SIZE);
		setHeight(DISPLAY_HEIGHT * VisualImage.SQUARE_SIZE);
	}

	public void start() {
		map = (Map) DataGroup.findMap("rook");
		requestFocus();
	}

	// Player
	private Player player() {
		Player player = null;
		if (this.getCentralObject() != null
				&& this.getCentralObject().getClass() == Creature.class) {
			player = ((Creature) this.getCentralObject()).getPlayer();
		}
		return player;
	}

	// Key Events
	public void onKeyPressed(KeyEvent event) {
		Player player = player();
		if (player != null)
			player.onKeyPress(event);
	}

	// Mouse Events
	@Override
	public void onMouseClicked(MouseEvent event) {
		if (!this.mouseInside(event.getX() - getParentLastX(), event.getY()
				- getParentLastY()))
			return;

		Player player = player();
		if (player != null)
			player.onMouseClick(event);
		requestFocus();
	}

	// Map
	public final Map getMap() {
		return map;
	}

	public void setMap(MapData map) {
		this.map = (Map) map;
	}

	// Draw Actions
	public void restartTranslation(Graphics2D g) {
		double translateX = -g.getTransform().getTranslateX();
		double translateY = -g.getTransform().getTranslateY();

		g.translate(translateX, translateY);
	}

	public void draw(Graphics2D g, Location location, int cicle,
			double startTranslateX, double startTranslateY,
			double externalTranslateX, double externalTranslateY) {
		// Initial Translation
		restartTranslation(g);
		g.translate(startTranslateX + externalTranslateX, startTranslateY
				+ externalTranslateY);

		// Translate
		double translateX = location.getX() * VisualImage.SQUARE_SIZE;
		double translateY = location.getY() * VisualImage.SQUARE_SIZE;
		g.translate(translateX, translateY);

		// Draw
		drawItems(g, location, cicle);

		float factor = (float) VisualImage.SQUARE_SIZE
				/ VisualImage.DEFAULT_SQUARE_SIZE;
		g.translate(-factor * 6, -factor * 6);
		drawCreature(g, location, cicle);
		drawEffects(g, location, cicle, startTranslateX, startTranslateY);
	}

	public void drawItems(Graphics2D g, Location location, int cicle) {
		int stackLimit = 4;
		int stackCount = 0;
		for (MapObjectData data : location.getItems()) {
			if (data == null || data.getOwner() == null)
				continue;

			MapObject object = (MapObject) data.getOwner();
			ItemData itemData = (ItemData) object.getData();
			stackCount += itemData.getStack();

			if ((stackCount <= stackLimit && cicle == 0)
					|| (cicle == 1 && stackCount > stackLimit)) {
				VisualImage.onPaint(g, object);
			}
			g.translate(-itemData.getStack(), -itemData.getStack());
		}
	}

	public void drawCreature(Graphics2D g, Location location, int cicle) {
		if (cicle != 1)
			return;

		if (location.getCreature() == null
				|| location.getCreature().getOwner() == null)
			return;

		MapObject object = (MapObject) location.getCreature().getOwner();
		VisualImage.onPaint(g, object);

		if (player() != null) {
			if (player().getCreature().getTarget() == object) {
				g.setColor(Color.red);
				g.drawRect(0, 0, VisualImage.SQUARE_SIZE,
						VisualImage.SQUARE_SIZE);
			}
		}
	}

	public void drawEffects(Graphics2D g, Location location, int cicle,
			double startTranslateX, double startTranslateY) {
		if (cicle != 2)
			return;

		for (MapObjectData data : location.getEffects()) {
			if (data == null || data.getOwner() == null)
				continue;

			MapObject object = (MapObject) data.getOwner();
			((Effect) object).onPaint(g);

			if (data.getSprite() != null)
				VisualImage.onPaint(g, object);
		}
	}

	// Event
	@Override
	public void onPaint(Graphics2D g) {
		super.onPaint(g);

		if (getMap() == null) {
			return;
		}

		int width = (int) Math.ceil(this.getWidth() / VisualImage.SQUARE_SIZE);
		int height = (int) Math
				.ceil(this.getHeight() / VisualImage.SQUARE_SIZE);

		Double startTranslateX = 0D, startTranslateY = 0D;
		Double externalTranslateX = 0D, externalTranslateY = 0D;
		if (this.getScroll()) {
			startTranslateX = g.getTransform().getTranslateX();
			startTranslateY = g.getTransform().getTranslateY();
		} else {
			externalTranslateX = g.getTransform().getTranslateX();
			externalTranslateY = g.getTransform().getTranslateY();
		}

		if (this.getCentralObject() != null) {
			// Position Translate
			startTranslateX -= this.getCentralObject().getLocation().getX()
					* VisualImage.SQUARE_SIZE;
			startTranslateY -= this.getCentralObject().getLocation().getY()
					* VisualImage.SQUARE_SIZE;

			startTranslateX += (width - 1) / 2 * VisualImage.SQUARE_SIZE;
			startTranslateY += (height - 1) / 2 * VisualImage.SQUARE_SIZE;

			// Walk Translate
			if (this.getCentralObject().getClass() == Creature.class) {
				Creature centralCreature = (Creature) this.getCentralObject();

				if (centralCreature.getSide() == MapObjectData.SIDE_RIGHT
						|| centralCreature.getSide() == MapObjectData.SIDE_RIGHT_DOWN
						|| centralCreature.getSide() == MapObjectData.SIDE_RIGHT_UP) {
					startTranslateX -= centralCreature.walkPercent()
							* VisualImage.SQUARE_SIZE / 100;
				}

				if (centralCreature.getSide() == MapObjectData.SIDE_LEFT
						|| centralCreature.getSide() == MapObjectData.SIDE_LEFT_DOWN
						|| centralCreature.getSide() == MapObjectData.SIDE_LEFT_UP) {
					startTranslateX += centralCreature.walkPercent()
							* VisualImage.SQUARE_SIZE / 100;
				}

				if (centralCreature.getSide() == MapObjectData.SIDE_DOWN
						|| centralCreature.getSide() == MapObjectData.SIDE_LEFT_DOWN
						|| centralCreature.getSide() == MapObjectData.SIDE_RIGHT_DOWN) {
					startTranslateY -= centralCreature.walkPercent()
							* VisualImage.SQUARE_SIZE / 100;
				}

				if (centralCreature.getSide() == MapObjectData.SIDE_UP
						|| centralCreature.getSide() == MapObjectData.SIDE_LEFT_UP
						|| centralCreature.getSide() == MapObjectData.SIDE_RIGHT_UP) {
					startTranslateY += centralCreature.walkPercent()
							* VisualImage.SQUARE_SIZE / 100;
				}
			}

			if (startTranslateX > 0)
				startTranslateX = 0D;

			if (startTranslateY > 0)
				startTranslateY = 0D;
		}

		// Loop Variables
		int startX = (int) Math.floor(Math.abs(startTranslateX)
				/ VisualImage.SQUARE_SIZE);

		int startY = (int) Math.floor(Math.abs(startTranslateY)
				/ VisualImage.SQUARE_SIZE);
		int startZ = 7;

		int endX = startX + width;
		int endY = startY + height;
		int endZ = 14;

		Player player = player();
		if (player != null) {
			player.setLastTranslateX(-startTranslateX - getLastX());
			player.setLastTranslateY(-startTranslateY - getLastY());
		}

		for (int cicle = 0; cicle < 3; cicle++) {
			for (int x = startX; x <= endX; ++x) {
				for (int y = startY; y <= endY; ++y) {
					for (int z = startZ; z <= endZ; ++z) {
						Location location = this.getMap().getLocation(x, y, z);
						if (location == null)
							continue;

						this.draw(g, location, cicle, startTranslateX,
								startTranslateY, externalTranslateX,
								externalTranslateY);
					}
				}
			}
		}
	}

	// Get & Set
	@Override
	public PVisual getVisual() {
		return super.getVisual();
	}

	@Override
	public void setVisual(PVisual visual) {
		super.setVisual(visual);
	}

	public MapObject getCentralObject() {
		return centralObject;
	}

	public void setCentralObject(MapObject centralObject) {
		this.centralObject = centralObject;
	}

	public Boolean getScroll() {
		return scroll;
	}

	public void setScroll(Boolean scroll) {
		this.scroll = scroll;
	}
}
